home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Applications / Python 1.3.3 / Python 133 68K / Lib / cgi.py < prev    next >
Text File  |  1996-05-20  |  9KB  |  304 lines

  1. #!/usr/local/bin/python
  2.  
  3. # XXX TODO
  4. # - proper doc strings instead of this rambling dialogue style
  5. # - more utilities, e.g.
  6. #   - print_header(type="test/html", blankline=1) -- print MIME header
  7. #   - utility to format a nice error message in HTML
  8. #   - utility to format a Location: ... response, including HTML
  9. #   - utility to catch errors and display traceback
  10.  
  11. #
  12. # A class for wrapping the WWW Forms Common Gateway Interface (CGI) 
  13. # Michael McLay, NIST  mclay@eeel.nist.gov  6/14/94
  14. # modified by Steve Majewski <sdm7g@Virginia.EDU> 12/5/94 
  15. #
  16. # now maintained as part of the Python distribution
  17.  
  18. # Several classes to parse the name/value pairs that are passed to 
  19. # a server's CGI by GET, POST or PUT methods by a WWW FORM. This 
  20. # module is based on Mike McLay's original cgi.py after discussing
  21. # changes with him and others on the comp.lang.python newsgroup, and
  22. # at the NIST Python workshop. 
  23. #
  24. # The rationale for changes was:
  25. #    The original FormContent class was almost, but not quite like
  26. #    a dictionary object. Besides adding some extra access methods,
  27. #    it had a values() method with different arguments and semantics
  28. #    from the standard values() method of a mapping object. Also, 
  29. #    it provided several different access methods that may be necessary
  30. #    or useful, but made it a little more confusing to figure out how
  31. #    to use. Also, we wanted to make the most typical cases the simplest
  32. #    and most convenient access methods. ( Most form fields just return
  33. #    a single value, and in practice, a lot of code was just assuming
  34. #    a single value and ignoring all others. On the other hand, the 
  35. #    protocol allows multiple values to be returned. 
  36. #
  37. #  The new base class (FormContentDict) is just like a dictionary.
  38. #  In fact, if you just want a dictionary, all of the stuff that was
  39. #  in __init__ has been extracted into a cgi.parse() function that will
  40. #  return the "raw" dictionary, but having a class allows you to customize 
  41. #  it further. 
  42. #   Mike McLay's original FormContent class is reimplemented as a 
  43. #  subclass of FormContentDict.
  44. #   There are two additional sub-classes, but I'm not yet too sure 
  45. #  whether they are what I want. 
  46.  
  47. import string,regsub,sys,os,urllib
  48. # since os.environ may often be used in cgi code, we name it in this module.
  49. from os import environ
  50.  
  51.  
  52. def parse():
  53.     """Parse the query passed in the environment or on stdin"""
  54.     if environ['REQUEST_METHOD'] == 'POST':
  55.         qs = sys.stdin.read(string.atoi(environ['CONTENT_LENGTH']))
  56.         environ['QUERY_STRING'] = qs
  57.     elif environ.has_key('QUERY_STRING'):
  58.         qs = environ['QUERY_STRING']
  59.     else:
  60.         environ['QUERY_STRING'] = qs = ''
  61.     return parse_qs(qs)
  62.  
  63.  
  64. def parse_qs(qs):
  65.     """Parse a query given as a string argument"""
  66.     name_value_pairs = string.splitfields(qs, '&')
  67.     dict = {}
  68.     for name_value in name_value_pairs:
  69.         nv = string.splitfields(name_value, '=')
  70.         if len(nv) != 2:
  71.             continue
  72.         name = nv[0]
  73.         value = urllib.unquote(regsub.gsub('+',' ',nv[1]))
  74.         if len(value):
  75.             if dict.has_key (name):
  76.                 dict[name].append(value)
  77.             else:
  78.                 dict[name] = [value]
  79.     return dict
  80.  
  81.  
  82.  
  83. # The FormContent constructor creates a dictionary from the name/value pairs
  84. # passed through the CGI interface.
  85.  
  86.  
  87. #
  88. #  form['key'] 
  89. #  form.__getitem__('key') 
  90. #  form.has_key('key')
  91. #  form.keys()
  92. #  form.values()
  93. #  form.items()
  94. #  form.dict
  95.  
  96. class FormContentDict:
  97.     def __init__( self ):
  98.         self.dict = parse()
  99.         self.query_string = environ['QUERY_STRING']
  100.     def __getitem__(self,key):
  101.         return self.dict[key]
  102.     def keys(self):
  103.         return self.dict.keys()
  104.     def has_key(self, key):
  105.         return self.dict.has_key(key)
  106.     def values(self):
  107.         return self.dict.values()
  108.     def items(self):
  109.         return self.dict.items() 
  110.     def __len__( self ):
  111.         return len(self.dict)
  112.  
  113.  
  114. # This is the "strict" single-value expecting version. 
  115. # IF you only expect a single value for each field, then form[key]
  116. # will return that single value ( the [0]-th ), and raise an 
  117. # IndexError if that expectation is not true. 
  118. # IF you expect a field to have possible multiple values, than you
  119. # can use form.getlist( key ) to get all of the values. 
  120. # values() and items() are a compromise: they return single strings
  121. #  where there is a single value, and lists of strings otherwise. 
  122.  
  123. class SvFormContentDict(FormContentDict):
  124.     def __getitem__( self, key ):
  125.         if len( self.dict[key] ) > 1 : 
  126.             raise IndexError, 'expecting a single value' 
  127.         return self.dict[key][0]
  128.     def getlist( self, key ):
  129.         return self.dict[key]
  130.     def values( self ):
  131.         lis = []
  132.         for each in self.dict.values() : 
  133.             if len( each ) == 1 : 
  134.                 lis.append( each[0] )
  135.             else: lis.append( each )
  136.         return lis
  137.     def items( self ):
  138.         lis = []
  139.         for key,value in self.dict.items():
  140.             if len(value) == 1 :
  141.                 lis.append( (key,value[0]) )
  142.             else:    lis.append( (key,value) )
  143.         return lis
  144.  
  145.  
  146. # And this sub-class is similar to the above, but it will attempt to 
  147. # interpret numerical values. This is here as mostly as an example,
  148. # but I think the real way to handle typed-data from a form may be
  149. # to make an additional table driver parsing stage that has a table
  150. # of allowed input patterns and the output conversion types - it 
  151. # would signal type-errors on parse, not on access. 
  152. class InterpFormContentDict(SvFormContentDict):
  153.     def __getitem__( self, key ):
  154.         v = SvFormContentDict.__getitem__( self, key )
  155.         if v[0] in string.digits+'+-.' : 
  156.             try:  return  string.atoi( v ) 
  157.             except ValueError:
  158.                 try:    return string.atof( v )
  159.                 except ValueError: pass
  160.         return string.strip(v)
  161.     def values( self ):
  162.         lis = [] 
  163.         for key in self.keys():
  164.             try:
  165.                 lis.append( self[key] )
  166.             except IndexError:
  167.                 lis.append( self.dict[key] )
  168.         return lis
  169.     def items( self ):
  170.         lis = [] 
  171.         for key in self.keys():
  172.             try:
  173.                 lis.append( (key, self[key]) )
  174.             except IndexError:
  175.                 lis.append( (key, self.dict[key]) )
  176.         return lis
  177.  
  178.  
  179. # class FormContent parses the name/value pairs that are passed to a
  180. # server's CGI by GET, POST, or PUT methods by a WWW FORM. several 
  181. # specialized FormContent dictionary access methods have been added 
  182. # for convenience.
  183.  
  184. # function                   return value
  185. #
  186. # form.keys()                     all keys in dictionary
  187. # form.has_key('key')             test keys existance
  188. # form[key]                       returns list associated with key
  189. # form.values('key')              key's list (same as form.[key])
  190. # form.indexed_value('key' index) nth element in key's value list
  191. # form.value(key)                 key's unstripped value 
  192. # form.length(key)                number of elements in key's list
  193. # form.stripped(key)              key's value with whitespace stripped
  194. # form.pars()                     full dictionary 
  195.  
  196.  
  197.  
  198. class FormContent(FormContentDict):
  199. # This is the original FormContent semantics of values,
  200. # not the dictionary like semantics. 
  201.     def values(self,key):
  202.         if self.dict.has_key(key):return self.dict[key]
  203.         else: return None
  204.     def indexed_value(self,key, location):
  205.         if self.dict.has_key(key):
  206.             if len (self.dict[key]) > location:
  207.                 return self.dict[key][location]
  208.             else: return None
  209.         else: return None
  210.     def value(self,key):
  211.         if self.dict.has_key(key):return self.dict[key][0]
  212.         else: return None
  213.     def length(self,key):
  214.         return len (self.dict[key])
  215.     def stripped(self,key):
  216.         if self.dict.has_key(key):return string.strip(self.dict[key][0])
  217.         else: return None
  218.     def pars(self):
  219.         return self.dict
  220.  
  221.  
  222.  
  223.  
  224.  
  225.  
  226. def print_environ_usage():
  227.     print """
  228. <H3>These operating system environment variables could have been 
  229. set:</H3> <UL>
  230. <LI>AUTH_TYPE
  231. <LI>CONTENT_LENGTH
  232. <LI>CONTENT_TYPE
  233. <LI>DATE_GMT
  234. <LI>DATE_LOCAL
  235. <LI>DOCUMENT_NAME
  236. <LI>DOCUMENT_ROOT
  237. <LI>DOCUMENT_URI
  238. <LI>GATEWAY_INTERFACE
  239. <LI>LAST_MODIFIED
  240. <LI>PATH
  241. <LI>PATH_INFO
  242. <LI>PATH_TRANSLATED
  243. <LI>QUERY_STRING
  244. <LI>REMOTE_ADDR
  245. <LI>REMOTE_HOST
  246. <LI>REMOTE_IDENT
  247. <LI>REMOTE_USER
  248. <LI>REQUEST_METHOD
  249. <LI>SCRIPT_NAME
  250. <LI>SERVER_NAME
  251. <LI>SERVER_PORT
  252. <LI>SERVER_PROTOCOL
  253. <LI>SERVER_ROOT
  254. <LI>SERVER_SOFTWARE
  255. </UL>
  256. """
  257.  
  258. def print_environ():
  259.     skeys = environ.keys()
  260.     skeys.sort()
  261.     print '<h3> The following environment variables ' \
  262.           'were set by the CGI script: </h3>'
  263.     print '<dl>'
  264.     for key in skeys:
  265.         print '<dt>', escape(key), '<dd>', escape(environ[key])
  266.     print '</dl>' 
  267.  
  268. def print_form( form ):
  269.     skeys = form.keys()
  270.     skeys.sort()
  271.     print '<h3> The following name/value pairs ' \
  272.           'were entered in the form: </h3>'
  273.     print '<dl>'
  274.     for key in skeys:
  275.         print '<dt>', escape(key), ':',
  276.         print '<i>', escape(`type(form[key])`), '</i>',
  277.         print '<dd>', escape(`form[key]`)
  278.     print '</dl>'
  279.  
  280. def escape( s ):
  281.     s = regsub.gsub('&', '&', s) # Must be done first
  282.     s = regsub.gsub('<', '<', s)
  283.     s = regsub.gsub('>', '>', s)
  284.     return s
  285.  
  286. def test( what ):
  287.     label = escape(str(what))
  288.     print 'Content-type: text/html\n\n'
  289.     print '<HEADER>\n<TITLE>' + label + '</TITLE>\n</HEADER>\n'
  290.     print '<BODY>\n' 
  291.     print "<H1>" + label +"</H1>\n"
  292.     form = what()
  293.     print_form( form )
  294.     print_environ()
  295.     print_environ_usage() 
  296.     print '</body>'
  297.  
  298. if __name__ == '__main__' : 
  299.     test_classes = ( FormContent, FormContentDict, SvFormContentDict, InterpFormContentDict )
  300.     test( test_classes[0] )    # by default, test compatibility with 
  301.                 # old version, change index to test others.
  302.